Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

performance and style advice requested

2 views
Skip to first unread message

Alex Martelli

unread,
Sep 14, 2003, 12:09:54 PM9/14/03
to
I'm trying to learn some Ruby, so I want to write some Ruby code, starting
with my usual favourite fun field -- combinatorial arithmetic related to
the game of contract bridge. As a warm-up, I begin with the "probability
of pointcount". Problem definition: a hand is a set of 13 cards out of 52.
16 of those cards (Aces, Kings, Queens, Jacks) are known as "honors" and
are assigned a "pointcount" (4 for each Ace, 3 for each King, 2 for each
Queen, 1 for each Jack). Consider all possible hands and determine the
exact probability of each possible point-count.

Basically, this boils down to considering each of the 2**16 possible
sets of honors -- each has an easily computed total pointcount -- and
for each of them computing in how many different ways it can occur --
which is C(36, 13-numberofhonors) -- the ways of choosing out of the
36 non-honors in the deck the 13-numberofhonors non-honors in this
hand. (That's with C(x,y) extended to be 0 for y<0, of course).

So I sit down and code it in the simplest way -- in Python first for
practice, since that's what I'm familiar with, then in Ruby. Fine
both ways, except that (on my laptop, which is all I have at hand as
I'm on a business trip -- with Python 2.3 and Ruby 1.8 and Win/XP)
the Python version runs in just above 4 seconds while the Ruby one
takes over 9. Hmmm, probably my lack of Ruby instincts, of course,
but anyway -- just as of course -- this needs to be optimized by
quite a bit (not for its own sake, but because later I'll extend it
to compute much more interesting stuff, such as correlations between
points, controls and shape, etc, etc) so who cares about performance
for the "coded in the simplest way" versions (oh, I should clarify:
simplest _except_ that I have of course memoized C(), computing
combinations aka binomial coefficients, and the factorial it calls).

So I start optimizing and squeezing every cycle I can -- and I get
down to about 0.65 seconds for Python, 2.3 seconds for Ruby (best
case, for each of them). Eep -- the ratio keeps increasing, so my
lack of sound Ruby instincts must be really hurting. Oh well, say
I, let's try "ruby -rprofile". EEK! After a couple of times where
I was convinced it had just seized up I decided to let it run all
the way -- and it took almost 1000 seconds. Is a slowdown of over
400 times normal for Ruby profiling, or is something weird in my
installation...? (It's the one-exe native Ruby 1.8 install for Win
which comes with a lot of goodies such as SciTE, Programming Ruby
in HTML-Help format, etc).

Anyway, the profiler's output isn't illuminating to me at all, so
that's when I decide to turn to the famous Ruby community -- I'm
sure you'll find lots to critique in my Ruby program, particularly
with an eye to performance but not necessarily just that (I _am_
quite aware that I may be guilty of "coding Python in Ruby" -- and
this may produce suboptimal performance _and_ other issues).

So, first, for reference, here's the optimized Python program:


#
# Memoized implementation of factorial
#
def fact(x, _fact_memo = {0:1, 1:1, 2:2, 3:6, 4:24, 5:120, 6:720, 7:5040} ):
" factorial of x (0 if x<0) "
try: return _fact_memo[x]
except KeyError:
if x < 0: return 0
return _fact_memo.setdefault(x, x * fact(x-1))

#
# Memoized implementation of C(x, y) [binomial coefficient, aka comb]
#
def comb(x, y, _comb_memo = {}):
" combinations of x things y at a time (0 if x<0, y<0, or y>x) "
try: return _comb_memo[x, y]
except KeyError:
if x<0 or y<0 or y>x: return 0
return _comb_memo.setdefault((x, y), fact(x) / (fact(y)*fact(x-y)))

#
# we represent a "honor" by a its points-worth
#
honors = [ p for p in range(1,5) for s in range(4) ]

#
# we represent a "honors set" by a pair (points-worth, total-#-of-cards)
#
def honor_subsets(lis, fromindex=0):
""" recursive generator of all sets from a list and starting point;
each set is represented by a honor-set as above, while the list
must hold points-worths representing "honors" again as above.
"""
ph = lis[fromindex]
if fromindex == len(lis)-1:
yield 0, 0
yield ph, 1
return
for pw, le in honor_subsets(lis, fromindex+1):
yield pw, le
yield ph+pw, 1+le

def main():
" main computation "
# hist: histogram points -> number of occurrences
hist = {}
# totcom: total number of occurrences
totcom = 0L
# range over all possible honorsets
for hs in honor_subsets(honors):
# compute number of honors and pointcount for this honorset
pt, nh = hs
# compute number of possible occurrences of this honorset
nc = comb(36, 13-nh)
# update histogram and total
hist[pt] = hist.get(pt, 0) + nc
totcom += nc
# print total and eyeball-check it
print totcom, comb(52, 13)

# sort histogram by decreasing frequency
aux = [ (nc, pt) for pt, nc in hist.iteritems() ]
aux.sort()
aux.reverse()

# compute frequencies as relative percentages
divis = 100.0 / totcom

# give top 10 possibilities
for nc, pt in aux[:10]:
print "%2d %d (%.2f)" % (pt, nc, nc * divis)


# run the computation
import time
start = time.time()
main()
stend = time.time()
print stend-start


and here is the corresponding optimized Ruby program:


#
# factorial, memoized via an Array
#
$_fact_memo=[1,1,2,6,24,120,720]
def fact(x)
# factorial of x (0 if x<0)
result = $_fact_memo[x]
if !result
return 0 if x<0
result = $_fact_memo[x] = x*fact(x-1)
end
return result
end

#
# binomial factor (aka comb), memoized via a Hash
#
$_comb_memo={}
def comb(x, y)
# combinations of x things y at a time (0 if x<0, y<0, or y>x)
result = $_comb_memo[[x, y]]
if !result
return 0 if x<0 or y<0 or y>x
result = $_comb_memo[[x, y]] = fact(x)/(fact(y)*fact(x-y))
end
return result
end

#
# add to Array a recursive iterator over honor-subsets
#
# a honor is represented by its point-value (1..4); a honor-subset is
# a pair [total-pointcount, number-of-honors]
#
class Array
def each_subset(from=0)
if from==size-1:
#
# end of array, so, return 2 only possible subsets remaining:
# empty set, or singleton set with the last element in it
#
yield [0, 0]
yield [self[from], 1]
else
#
# 2+ elements remaining, so, loop iteratively, extracting
# the current element and getting all possible subsets of
# all later ones -- for each of those, return 2 possible
# subsets, i.e., either without or with the current element
#
thispoints = self[from]
each_subset(from+1) do |subpoints, sublength|
yield subpoints, sublength
yield thispoints + subpoints, 1 + sublength
end
end
end
end


def main()
# main computation
# hist: histogram (Hash) points -> number of occurrences
hist = Hash.new(0)
# totcon: total number of occurrences
totcom = 0
# range over all possible honor-sets
honors = Array(1..4) * 4
honors.each_subset do |pt, nh|
# receive number of honors and pointcount for this honorset,
# then compute number of possible occurrences of this honorset
nc = comb(36, 13-nh)
# update histogram and total
hist[pt] += nc
totcom += nc
end
# print total and eyeball-check it
print totcom, ' ', comb(52, 13), "\n"

# sort histogram by decreasing frequency
aux = hist.sort {|a,b| b[1]<=>a[1]}

# compute frequencies as relative percentages
divis = totcom / 100.0

# give top 10 possibilities
for pt, nc in aux[0,10]
printf "%2d %d (%.2f)\n" , pt, nc, nc/divis
end
end

# run the computation
start = Time.now
main
stend = Time.now
puts stend-start


Finally, FWIW, here's the start of the profiler's output...:


C:\Python23>ruby -rprofile alco2.rb
635013559600 635013559600
10 59723754816 (9.41)
9 59413313872 (9.36)
11 56799933520 (8.94)
8 56466608128 (8.89)
7 50979441968 (8.03)
12 50971682080 (8.03)
13 43906944752 (6.91)
6 41619399184 (6.55)
14 36153374224 (5.69)
5 32933031040 (5.19)
948.294
% cumulative self self total
time seconds seconds calls ms/call ms/call name
34.90 317.57 317.57 16 19848.44 909790.50 Array#each_subset
13.12 437.00 119.43 65537 1.82 6.56 Object#comb
10.56 533.11 96.11 131073 0.73 2.54 Hash#[]
8.20 607.70 74.59 65385 1.14 1.82 Array#eql?
8.11 681.49 73.79 65552 1.13 1.79 Array#hash
6.17 737.65 56.16 161735 0.35 0.35 Fixnum#+
4.87 781.99 44.34 130770 0.34 0.34 Numeric#eql?
4.80 825.63 43.65 131104 0.33 0.33 Kernel.hash
4.14 863.31 37.68 100426 0.38 0.38 Bignum#+
2.59 886.84 23.53 65551 0.36 0.36 Hash#[]=
2.48 909.38 22.54 65613 0.34 0.34 Fixnum#-
0.02 909.57 0.19 91 2.09 38.57 Object#fact
0.02 909.75 0.18 1 180.00 180.00
Profiler__.start_profile
0.02 909.92 0.17 1 170.00 190.00 Hash#sort
0.01 910.02 0.10 350 0.29 0.29 Fixnum#<
0.01 910.10 0.08 193 0.41 0.41 Hash#default
0.01 910.16 0.06 59 1.02 1.19 Fixnum#*

I've truncated starting from where the "%time" goes to 0.00.


Thanks for any feedback!


Alex

Simon Strandgaard

unread,
Sep 14, 2003, 2:12:40 PM9/14/03
to
On Sun, 14 Sep 2003 19:09:54 +0200, Alex Martelli wrote:
[snip]
> Anyway, the profiler's output isn't illuminating to me at all, so
> that's when I decide to turn to the famous Ruby community -- I'm
> sure you'll find lots to critique in my Ruby program, particularly
> with an eye to performance but not necessarily just that (I _am_
> quite aware that I may be guilty of "coding Python in Ruby" -- and
> this may produce suboptimal performance _and_ other issues).
[snip]
> #
> # binomial factor (aka comb), memoized via a Hash
> #
> $_comb_memo={}
> def comb(x, y)
> # combinations of x things y at a time (0 if x<0, y<0, or y>x)
> result = $_comb_memo[[x, y]]

^^^^^^
^^^^^^

very slow!!

Ruby converts it the array into a string.. this takes time!


> if !result
> return 0 if x<0 or y<0 or y>x
> result = $_comb_memo[[x, y]] = fact(x)/(fact(y)*fact(x-y))
> end
> return result
> end


Try this instead, and tell me if it works ?


def comb(x, y)
# combinations of x things y at a time (0 if x<0, y<0, or y>x)

result = $_comb_memo[x << 16 + y]


if !result
return 0 if x<0 or y<0 or y>x

result = $_comb_memo[x << 16 + y] = fact(x)/(fact(y)*fact(x-y))
end
return result
end

--
Simon Strandgaard

Ben Giddings

unread,
Sep 14, 2003, 6:29:40 PM9/14/03
to
Some style advice:

On Sunday, September 14, 2003, at 12:44 PM, Alex Martelli wrote:
> #
> # factorial, memoized via an Array
> #
> $_fact_memo=[1,1,2,6,24,120,720]

Ugh. This is what really bothers me about python programs. Ugly
underscores.

> def fact(x)
> # factorial of x (0 if x<0)
> result = $_fact_memo[x]
> if !result
> return 0 if x<0
> result = $_fact_memo[x] = x*fact(x-1)
> end
> return result
> end

Hmm, how about

def fact(x)
$fact_memo[x] ||= if x<0
0
else
x*fact(x-1)
end
end

Although I'm pretty sure that a simple conditional is faster than an
array lookup so this might be faster (and is easier to read):

def fact(x)


return 0 if x<0

$fact_memo[x] ||= x*fact(x-1)
end

You can take advantage of the fact Ruby expressions return their last
value, and the return value of a function is the last expression's
value.

The rest is more complex so I'll quit while I'm ahead. :)

Ben


Robert Klemme

unread,
Sep 15, 2003, 5:24:00 AM9/15/03
to

"Ben Giddings" <bg-ru...@infofiend.com> schrieb im Newsbeitrag
news:059CAA7E-E703-11D7...@infofiend.com...

Since recursions are costly and abort the interpreter for large n I
suggest this implementation:

def fact(n)
return 0 if n<1

@facts ||= [0,1]

last = @facts.size - 1

for i in @facts.size .. n
@facts[i] = @facts[last] * i
last = i
end

@facts[n]
end


Regards

robert

Robert Feldt

unread,
Sep 15, 2003, 7:25:59 AM9/15/03
to
Alex Martelli <ale...@yahoo.com> skrev den Mon, 15 Sep 2003 01:44:09 +0900:

> So I start optimizing and squeezing every cycle I can -- and I get
> down to about 0.65 seconds for Python, 2.3 seconds for Ruby (best
> case, for each of them). Eep -- the ratio keeps increasing, so my
> lack of sound Ruby instincts must be really hurting. Oh well, say
> I, let's try "ruby -rprofile". EEK! After a couple of times where
> I was convinced it had just seized up I decided to let it run all
> the way -- and it took almost 1000 seconds. Is a slowdown of over
> 400 times normal for Ruby profiling, or is something weird in my
> installation...? (It's the one-exe native Ruby 1.8 install for Win
> which comes with a lot of goodies such as SciTE, Programming Ruby
> in HTML-Help format, etc).
>

No, slowdowns in the range 20-500 times (depending on the code) is
to be expected. For speedier profiling you can try my rbprof profiler
which is part of AspectR. I haven't updated it in 1.5 years or so
though so it might not work out-of-the-box. Its faster and gives more
information.

> Anyway, the profiler's output isn't illuminating to me at all, so
> that's when I decide to turn to the famous Ruby community -- I'm
> sure you'll find lots to critique in my Ruby program, particularly
> with an eye to performance but not necessarily just that (I _am_
> quite aware that I may be guilty of "coding Python in Ruby" -- and
> this may produce suboptimal performance _and_ other issues).
>

While I doubt you should compare these languages on performance merits
and sincerely hope this post is not a subtle troll post (I assume it
isn't so: Welcome to the Ruby community, Alex!) let's give this a
try.

When it comes to performance I would first look at the algorithm. what
strikes me is that you can also parameterize on the point count
for each honor. This way you would not need to traverse all 2**16
combinations.

Second observation is that you should use Process.times.utime to time
this since you only want the time spent by the Ruby process.

Third observation is that I don't like globals so I wrap the
fact and comb methods in a module.

Fourth is some minor changes to use common Ruby idioms.

So my version is:

module Comb
# memoize results for speed
FactMemo, CombMemo = [1, 1, 2, 6, 24, 120, 720], {}

# factorial
def Comb.fact(n)
return 0 if n < 0
FactMemo[n] ||= (n * fact(n-1))
end

# binomial factor (aka comb), memoized via a Hash

def Comb.comb(x, y)


return 0 if x<0 or y<0 or y>x

CombMemo[[x,y]] ||= fact(x)/(fact(y) * fact(x-y))
end
end

#
# add to Array a recursive iterator over honor-value-subsets
#
# a honor is represented by its point-value (1..4); a honor-value-subset is
# [total-pointcount, number-of-honors, number-of-combs]


#
class Array
def each_subset(from=0)

v = self[from]
if from == length
yield 0, 0, 1
else
each_subset(from+1) do |pointcount, num_honors, num_combs|
(0..4).each do |m| yield pointcount + m*v, num_honors + m, num_combs * Comb.comb(4,m)
end


end
end
end
end
def main

# hist: histogram (Hash) points -> number of occurrences

histogram = Hash.new(0)


# totcon: total number of occurrences

total_count = 0
# range over all possible honor-value-subsets
honor_values = (1..4).to_a
honor_values.each_subset do |pt, nh, count|
# receive number of honors and pointcount and combinations
# for this honor-value-set, then compute number of possible occurrences # of this honor-value-set
num_combs = count * Comb.comb(36, 13-nh)

# update histogram and total

histogram[pt] += num_combs
total_count += num_combs
end

# print total and eyeball-check it

print total_count, ' ', Comb.comb(52, 13), "\n"


# sort histogram by decreasing frequency

aux = histogram.sort {|a,b| b[1]<=>a[1]}


# compute frequencies as relative percentages

divisor = total_count / 100.0


# give top 10 possibilities

aux[0,10].each do |point, count|
printf "%2d %d (%.2f)\n" , point, count, count / divisor
end
end

def time
start = Process.times.utime
yield
stend = Process.times.utime
stend-start
end

elapsed = time {main}
puts "#{elapsed} seconds"

and comparing that to your version (only changed to use Process.times.utime)
I get:

$ time ruby am_better_timing.rb


635013559600 635013559600
10 59723754816 (9.41)
9 59413313872 (9.36)
11 56799933520 (8.94)
8 56466608128 (8.89)
7 50979441968 (8.03)
12 50971682080 (8.03)
13 43906944752 (6.91)
6 41619399184 (6.55)
14 36153374224 (5.69)
5 32933031040 (5.19)

0.531

real 0m0.553s
user 0m0.561s
sys 0m0.000s

feldt@novomundo1 /tmp/alex_martelli
$ time ruby feldt.rb


635013559600 635013559600
10 59723754816 (9.41)
9 59413313872 (9.36)
11 56799933520 (8.94)
8 56466608128 (8.89)
7 50979441968 (8.03)
12 50971682080 (8.03)
13 43906944752 (6.91)
6 41619399184 (6.55)
14 36153374224 (5.69)
5 32933031040 (5.19)

0.0 seconds

real 0m0.033s
user 0m0.062s
sys 0m0.000s

and I'm ok with that so I'll stop there for now. But the change
I did in the algorithm is also available for you in python so I guess
the ratio between the languages might not change much...

Best regards,

Robert Feldt

Lyle Johnson

unread,
Sep 15, 2003, 9:30:33 AM9/15/03
to ale...@yahoo.com
Robert Feldt wrote:

> While I doubt you should compare these languages on performance merits

> and sincerely hope this post is not a subtle troll post...

I think Alex's reputation in the Python community more than speaks for
the fact that he's *not* a troll. This is a very reasonable question
about Python versus Ruby performance.

Ben Giddings

unread,
Sep 15, 2003, 9:35:14 AM9/15/03
to
Robert Feldt wrote:
> Fourth is some minor changes to use common Ruby idioms.
>
> So my version is:
>
> module Comb
> # memoize results for speed
> FactMemo, CombMemo = [1, 1, 2, 6, 24, 120, 720], {}
>
> # factorial
> def Comb.fact(n)
> return 0 if n < 0
> FactMemo[n] ||= (n * fact(n-1))
> end

I like the fact you wrapped the functionality in a module, but if we're
using Ruby idioms, the FactMemo and CombMemo variables should really not
be named with leading uppercase letters. They look like classes (or
constants) and may be treated as such by Ruby. I'd recommend instead
fact_memo and comb_memo.

Ben


Robert Feldt

unread,
Sep 15, 2003, 10:15:30 AM9/15/03
to

Ok. I hope it was clear that I wasn't implying that he is.
My remark has nothing to do with Alex really; recent threads here on the list just makes "comparisons" between Ruby and Python a bit "infected" right now I guess. ;)

Sorry if it came out the wrong way.

Then I guess we should be delighted that a python "hot-shot"
is willing to check Ruby out. Again: Welcome here, Alex.

Regards,

Robert

Robert Feldt

unread,
Sep 15, 2003, 10:23:35 AM9/15/03
to
Ben Giddings <bg-ru...@infofiend.com> skrev den Mon, 15 Sep 2003 22:35:14 +0900:

>> So my version is:
>>
>> module Comb
>> # memoize results for speed
>> FactMemo, CombMemo = [1, 1, 2, 6, 24, 120, 720], {}
>>
>> # factorial
>> def Comb.fact(n)
>> return 0 if n < 0
>> FactMemo[n] ||= (n * fact(n-1))
>> end
>
> I like the fact you wrapped the functionality in a module, but if we're using Ruby idioms, the FactMemo and CombMemo variables should really not be named with leading uppercase letters. They look like classes (or constants) and may be treated as such by Ruby. I'd recommend instead fact_memo and comb_memo.
>

Yeah, I guess you're right in a way. On the other hand they *are* constants since the array and the hash are the same objects
even if their contents change... ;)

To me its more a question of what feels right than its about
what is more of a Ruby idiom. I could be wrong though...

The more logical to me following your thinking would be @fact_memo
and @comb_memo. Or to use the memoize extension. :)

/Robert

Alex Martelli

unread,
Sep 15, 2003, 12:40:00 PM9/15/03
to
Ben Giddings wrote:

> Some style advice:
>
> On Sunday, September 14, 2003, at 12:44 PM, Alex Martelli wrote:
>> #
>> # factorial, memoized via an Array
>> #
>> $_fact_memo=[1,1,2,6,24,120,720]
>
> Ugh. This is what really bothers me about python programs. Ugly
> underscores.

Heh -- matter of taste, I guess; what _I_ personally find ugly is
that leading $, and I left the underscores in to try to ameliorate
that. Of course, nothing stops you from moving to a camelCaseish
$factMemo -- however, not only do I personally find that less
readable, but I'd also like to leave a hint that the memo is an
internal implementation detail, not a feature meant for external
consumption, and I like the convention of a leading _ for that.
(Of course, I _could_ wrap up the fact function and its memo in
a single package in any of various ways, I'm sure, so the "hint"
aspect is quite secondary).


>> def fact(x)
>> # factorial of x (0 if x<0)
>> result = $_fact_memo[x]
>> if !result
>> return 0 if x<0
>> result = $_fact_memo[x] = x*fact(x-1)
>> end
>> return result
>> end
>
> Hmm, how about
>
> def fact(x)
> $fact_memo[x] ||= if x<0
> 0
> else
> x*fact(x-1)
> end
> end
>
> Although I'm pretty sure that a simple conditional is faster than an
> array lookup so this might be faster (and is easier to read):

However, it will be very rare for any given x to NOT be already
memoized (i.e., fact is called with a very modest variety of
arguments, compared to the number of calls to it, in any typical
combinatorial-arithmetic application); while calls with x<0 are
going to be exceedingly rare. So, the relative costs of array
indexing vs (operator< + conditional) shouldn't matter -- trying
to get the result off the array first _should_ be a win anyway
(or at least, that's what my optimization experience as built on
a wide variety of languages tells me -- admittedly I have no such
experience in Ruby, but I fail to see how it would differ on this
specific point).


> def fact(x)
> return 0 if x<0
> $fact_memo[x] ||= x*fact(x-1)
> end

Actually, I find the oneliner body

$_fact_memo[x] ||= if x<0 then 0 else x * fact(x-1) end

quite clear and readable, given that one must grok the semantics
of ||= anyway.


> You can take advantage of the fact Ruby expressions return their last
> value, and the return value of a function is the last expression's
> value.

Very good point, and an excellent suggestion at least for concision &
readability purposes -- I'll definitely keep the idiom
<container>[<index>] ||= <value>
in mind for the future -- thanks!

Performance-wise, there ain't all that much in it for this app.

I've managed to scrounge some diskspace on Linux and compile both
Python and Ruby (1.6.8 only, unfortunately -- the link I downloaded
from mentioned 1.8, but I found out only when everything was built
that I had 1.6.8 instead [how I found out: I had thoughtlessly left
a trailing colon on an "if" statement -- 1.8 was quite cool about
it, while 1.6.8 gave me three weird errors for that one mistake;-)].

First observation is that the performance ratio is only 2:1 in
favour of Python, not 3:1 as in the Windows version -- thus I
strongly suspect that part of the issue with the Windows version
is that it may not be compiled as optimally as Python is. Second
observation is that on Linux I can run the benchmarks under the
time command so I get an objective measurement of overall elapsed
time as well as what the program itself tells me about it -- i.e.
measuring all of the startup overhead -- and that is, by a tiny
margin, favourable to Ruby too.

So, under these conditions, I get:
for Python 2.3: 0.694 reported, 0.788 total
for Ruby 1.6.8:
my original code: 1.47 reported, 1.52 total
new and improved: 1.45 reported, 1.50 total

each of these measurements is the best out of many, many runs
(as I'm measuring elapsed times and can't put this Linux box in
anything like "isolated benchmarking conditions", unsurprisingly
the numbers over a few dozens of run are all over the place --
but the "best out of 24 runs" IS quite repeatable, so that giving
3 digits' precision in the reported results is just about right).

So, I tried applying the same idea to the comb method, makint it:

$_comb_memo={}
def comb(x, y)
# combinations of x things y at a time (0 if x<0, y<0, or y>x)

$_comb_memo[[x, y]] ||=

if x<0 or y<0 or y>x

then 0
else fact(x)/(fact(y)*fact(x-y))
end
end

(the if-else expression is too long for a oneliner here, so I had to
fold it), and that does give a slightly more substantial improvement:

for Ruby 1.6.8:
further improved: 1.38 reported, 1.44 total

I also tried bending it the other way as you recommended for fact:

$_comb_memo={}
def comb(x, y)
# combinations of x things y at a time (0 if x<0, y<0, or y>x)

if x<0 or y<0 or y>x then 0
else $_comb_memo[[x, y]] ||= fact(x)/(fact(y)*fact(x-y)) end
end

but that slows it down substantially (to 1.52 reported, 1.58 total,
best case) so I regressed this last change.


> The rest is more complex so I'll quit while I'm ahead. :)

Thanks, though! The performance has not changed by much, but I do
see the style is now more Ruby-ish, and that, after all, is half of
what I'm trying to learn (the other half being how to speed it up:-).


Alex

Alex Martelli

unread,
Sep 15, 2003, 12:53:24 PM9/15/03
to
Simon Strandgaard wrote:
...

>> def comb(x, y)
>> # combinations of x things y at a time (0 if x<0, y<0, or y>x)
>> result = $_comb_memo[[x, y]]
>
> ^^^^^^
> ^^^^^^
>
> very slow!!
>
> Ruby converts it the array into a string.. this takes time!

Oh my! I had no idea Ruby suffered under such a handicap wrt to Python
(that arrays had to be converted to strings to index into hashes, while
Python can use "tuples", its equivalent of frozen arrays, for that) --
then I must definitely be very sparing in using hashes with multi-dim
indexes in Ruby, at least in any bottleneck. Thanks! Just the kind of
things I'm trying to learn (I don't remember noticing any such caveat
in Thomas and Hunt's excellent book).

>
> Try this instead, and tell me if it works ?
>
>
> def comb(x, y)
> # combinations of x things y at a time (0 if x<0, y<0, or y>x)
> result = $_comb_memo[x << 16 + y]
> if !result
> return 0 if x<0 or y<0 or y>x
> result = $_comb_memo[x << 16 + y] = fact(x)/(fact(y)*fact(x-y))
> end
> return result
> end

I had already reworked comb (as per other advice from c.l.ruby( to a
slightly different and slightly faster form than my original one:

def comb(x, y)
# combinations of x things y at a time (0 if x<0, y<0, or y>x)

$_comb_memo[[x, y]] ||=
if x<0 or y<0 or y>x then 0
else fact(x)/(fact(y)*fact(x-y)) end
end

this gave a best-case performance (on Linux, and w. Ruby 1.6.8 -- a
far faster combo than Ruby 1.8.0 on Win/XP, apparently, see my latest
post) of 1.38 reported, 1.44 total.

The tiny change you suggest for indexing into the hash, i.e. changing
the only occurrence of indexing in this form of the method to

$_comb_memo[x<<16 + y] ||=

does make things even better -- we're now at 1.20 reported, 1.25 total,
best run of many (against 0.694 reported, 0.788 total for Python).
"Rubier and rubier", said Alice!-)

With Ruby now taking less than twice as long as Python, I guess I could
be satisfied here and move on to richer and more complicated cases, but
I can't help wondering if there might not be some crucial optimization
waiting to happen in the recursive iterator, which is (according to the
profiler, with its 400-times slowdown;-) by far the bottleneck...
any suggestions are welcome!


And thanks again for the suggestions so far...!


Alex

ts

unread,
Sep 15, 2003, 1:30:46 PM9/15/03
to
>>>>> "A" == Alex Martelli <ale...@yahoo.com> writes:

A> Oh my! I had no idea Ruby suffered under such a handicap wrt to Python
A> (that arrays had to be converted to strings to index into hashes, while
A> Python can use "tuples", its equivalent of frozen arrays, for that) --

svg% ruby -e 'a = {[1, 2] => 12}; p a.keys[0].class'
Array
svg%

A> then I must definitely be very sparing in using hashes with multi-dim
A> indexes in Ruby, at least in any bottleneck. Thanks! Just the kind of
A> things I'm trying to learn

then forget it :-)


Guy Decoux

Alex Martelli

unread,
Sep 15, 2003, 1:08:10 PM9/15/03
to
Robert Feldt wrote:

> Lyle Johnson <ly...@knology.net> skrev den Mon, 15 Sep 2003 22:47:12 +0900:
>
>>> While I doubt you should compare these languages on performance merits

*blink* why not, pray? While clarity, productivity, simplicity, and so on,
are surely all crucial attributes in a language, what's wrong with wanting
to ascertain how it performs (when used in the best/fastest way, at least)
in typical problems in one's domains of interest...? A 10% or 20%
difference is hardly ever going to matter, but factors of 2 or 3 might
well make the difference, in practice, between two languages of otherwise
comparable merits -- and that's what I am observing here, so far (at least
in the toy/starting problem which I'm using as a warm-up exercise -- we'll
see what happens when I move to bigger and more interesting problems, e.g.
I cannot exclude that Ruby's performance might "scale up" better, or
whatever).


>>> and sincerely hope this post is not a subtle troll post...
>>
>> I think Alex's reputation in the Python community more than speaks for
>> the fact that he's *not* a troll. This is a very reasonable question
>> about Python versus Ruby performance.
>>
> Ok. I hope it was clear that I wasn't implying that he is.
> My remark has nothing to do with Alex really; recent threads here on the
> list just makes "comparisons" between Ruby and Python a bit "infected"
> right now I guess. ;)

Were there highly contentious threads on Ruby vs Python here recently?
Sorry, I did look around briefly for such recent things (mostly to avoid
re-asking what might have recently been answered) but didn't find them
(maybe my ISP has expired them already, or maybe I made some mistake).


> Sorry if it came out the wrong way.

No problem!

> Then I guess we should be delighted that a python "hot-shot"
> is willing to check Ruby out. Again: Welcome here, Alex.

Thanks! I'm always willing to check out new things (or else I'd hardly
have found Python 3+ years ago, being somewhat of a C++ "hot-shot" then;-),
and was interested in Python since Linux Magazine (without telling me in
advance) ran my Python article and Thomas and Hunt's Ruby article smack
one against the other in the same issue. So I was quite glad to have the
opportunity to take Thomas's Ruby tutorial at OSCON, get and study the
pickaxe book, and starting to try things out for real -- the only way to
really learn a language (or other technology or tool), IMHO, particularly
with good support from an online community such as this one.


Alex

Hal Fulton

unread,
Sep 15, 2003, 1:38:26 PM9/15/03
to
Alex Martelli wrote:
> Simon Strandgaard wrote:
> ...
>
>>>def comb(x, y)
>>> # combinations of x things y at a time (0 if x<0, y<0, or y>x)
>>> result = $_comb_memo[[x, y]]
>>
>> ^^^^^^
>> ^^^^^^
>>
>> very slow!!
>>
>>Ruby converts it the array into a string.. this takes time!
>
>
> Oh my! I had no idea Ruby suffered under such a handicap wrt to Python
> (that arrays had to be converted to strings to index into hashes, while
> Python can use "tuples", its equivalent of frozen arrays, for that) --
> then I must definitely be very sparing in using hashes with multi-dim
> indexes in Ruby, at least in any bottleneck. Thanks! Just the kind of
> things I'm trying to learn (I don't remember noticing any such caveat
> in Thomas and Hunt's excellent book).

Simon is mistaken here. There may be some kind of
speed hit when storing an array as a hash key, but
the key is definitely stored as an array.

Hal


Robert Feldt

unread,
Sep 15, 2003, 1:57:24 PM9/15/03
to
Alex Martelli <ale...@yahoo.com> skrev den Tue, 16 Sep 2003 02:48:00 +0900:

>>>> While I doubt you should compare these languages on performance merits
>
> *blink* why not, pray? While clarity, productivity, simplicity, and so on,
> are surely all crucial attributes in a language, what's wrong with wanting
> to ascertain how it performs (when used in the best/fastest way, at least)
> in typical problems in one's domains of interest...? A 10% or 20% difference is hardly ever going to matter, but factors of 2 or 3 might
> well make the difference, in practice, between two languages of otherwise
> comparable merits -- and that's what I am observing here, so far (at least
> in the toy/starting problem which I'm using as a warm-up exercise -- we'll
> see what happens when I move to bigger and more interesting problems, e.g.
> I cannot exclude that Ruby's performance might "scale up" better, or whatever).
>

Nothing wrong with the comparison but:

1, If performance is *REALLY* important to you, you wouldn't use Python or Ruby anyway, IMHO, so their relative performance
is less important than any of them compared to C.

2, Performance is hard to compare since algorithmic differences is often
so much more important than the gains you get with "tweak-like"
performance tuning (for example the 16-time speedup of your problem I presented earlier)

3, There has been a couple of troll-like "Ruby has nothing to offer
compared to Python" threads in the last couple of weeks

so please understand my response in that context.

Regards,

Robert

Robert Feldt

unread,
Sep 15, 2003, 2:14:05 PM9/15/03
to
Dave Brown <dagb...@LART.ca> skrev den Tue, 16 Sep 2003 02:48:04 +0900:

> def fact(x)
> @facts[x] ||= (1..x).inject(1) { |a,i| a*i }
> end

Nice one. With the risc of losing readability here is an even
more efficient one:

@facts = [1]
def fact(n)
@facts[n] ||= (@facts.last..n).inject(1) {|f, i| @facts[i] = i * f}
end

since you won't need to recalc more than the ones between largest memoized
and your new one. In practice this tweak would probably make
very little difference... ;)

/Robert

Robert Feldt

unread,
Sep 15, 2003, 2:22:32 PM9/15/03
to
Robert Feldt <fe...@ce.chalmers.se> skrev den Tue, 16 Sep 2003 03:14:05 +0900:

> @facts = [1]
> def fact(n)
> @facts[n] ||= (@facts.last..n).inject(1) {|f, i| @facts[i] = i * f}
> end
>

Oops, it should be

@facts = [1]
def fact(n)

@facts[n] ||= (@facts.length..n).inject(@facts.last) {|f, i| @facts[i] = i * f}
end

/R

Austin Ziegler

unread,
Sep 15, 2003, 3:45:39 PM9/15/03
to
On Tue, 16 Sep 2003 03:22:32 +0900, Robert Feldt wrote:

I would change this to:

> def fact(n)
@facts ||= [1]


> @facts[n] ||= (@facts.length..n).inject(@facts.last) { |f, i|
> @facts[i] = i * f
> }
> end

-austin
--
austin ziegler * aus...@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.09.15
* 15.43.57

Nikolai Weibull

unread,
Sep 15, 2003, 3:47:27 PM9/15/03
to
* Austin Ziegler <aus...@halostatue.ca> [Sep, 15 2003 21:40]:
> Why not use a constant?
why use a constant?
nikolai

--
::: name: Nikolai Weibull :: aliases: pcp / lone-star / aka :::
::: born: Chicago, IL USA :: loc atm: Gothenburg, Sweden :::
::: page: www.pcppopper.org :: fun atm: gf,lps,ruby,php,war3 :::
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

Robert Klemme

unread,
Sep 15, 2003, 4:04:39 PM9/15/03
to

"Robert Feldt" <fe...@ce.chalmers.se> schrieb im Newsbeitrag
news:oprvjzzq...@mail1.telia.com...

> Robert Feldt <fe...@ce.chalmers.se> skrev den Tue, 16 Sep 2003 03:14:05
+0900:
>
> > @facts = [1]
> > def fact(n)
> > @facts[n] ||= (@facts.last..n).inject(1) {|f, i| @facts[i] = i * f}
> > end
> >
> Oops, it should be
>
> @facts = [1]

IMHO rather:
@facts = [0,1]

> def fact(n)
> @facts[n] ||= (@facts.length..n).inject(@facts.last) {|f, i| @facts[i] =
i * f}
> end

And, I think there's a subtle bug with the inject parameter. Another
version:

def fact(n)
@facts ||= [0,1]
@facts[n] ||= (@facts.length..n).inject(@facts[-1]) {|f, i| @facts[i] = i
* f}
end

And: Dave, thanks for this inspiring reduction! I haven't grown familiar to
inject() yet...

Kind regards

robert

Robert Feldt

unread,
Sep 15, 2003, 4:55:08 PM9/15/03
to
Robert Klemme <bob....@gmx.net> skrev den Tue, 16 Sep 2003 05:08:35 +0900:

>> def fact(n)
>> @facts[n] ||= (@facts.length..n).inject(@facts.last) {|f, i| @facts[i] =
> i * f}
>> end
>
> And, I think there's a subtle bug with the inject parameter. Another
> version:
>
> def fact(n)
> @facts ||= [0,1]
> @facts[n] ||= (@facts.length..n).inject(@facts[-1]) {|f, i| @facts[i] = i
> * f}
> end
>

Can you spell it out for me? I fail to see the difference.

And I wouldn't do the initial assignment inside the method when
we're discussing performance...

Regards,

/Robert

Austin Ziegler

unread,
Sep 15, 2003, 4:56:57 PM9/15/03
to
On Tue, 16 Sep 2003 04:47:27 +0900, Nikolai Weibull wrote:
> * Austin Ziegler <aus...@halostatue.ca> [Sep, 15 2003 21:40]:
>> Why not use a constant?
> why use a constant?

Because it looks better than a global, and exhibits what I consider to be
better behaviour overall.

-austin
--
austin ziegler * aus...@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.09.15

* 16.55.48

Nikolai Weibull

unread,
Sep 15, 2003, 5:05:08 PM9/15/03
to
* Austin Ziegler <aus...@halostatue.ca> [Sep, 15 2003 23:00]:

> > why use a constant?
>
> Because it looks better than a global, and exhibits what I consider to be
> better behaviour overall.
but a constant is a constant. constants should be used for constant
values, not things that change. i don't see how a constant behaves any
better than a global in this case. it's a, what, 100 line script,
globals may pass. to be good style, the whole thing should be kept in
some class somewhere,

Martin DeMello

unread,
Sep 15, 2003, 5:07:19 PM9/15/03
to
Dave Brown <dagb...@lart.ca> wrote:
>
> It just makes it worse. The $ is ugly for a reason--the use of
> global variables is discouraged. The Ruby Way to do it would be

Ironically, I find @var far uglier than $var - the @ is denser and
blockier, so it takes up a disproportionate amount of visual attention.

martin

Weirich, James

unread,
Sep 15, 2003, 5:13:33 PM9/15/03
to
> IMHO rather:
> @facts = [0,1]

Hmmm ... I'm pretty sure 0! = 1

--
-- Jim Weirich / Compuware
-- FWP Capture Services
-- Phone: 859-386-8855

Nikolai Weibull

unread,
Sep 15, 2003, 5:15:25 PM9/15/03
to
* Martin DeMello <martin...@yahoo.com> [Sep, 15 2003 23:10]:

> > It just makes it worse. The $ is ugly for a reason--the use of
> > global variables is discouraged. The Ruby Way to do it would be
>
> Ironically, I find @var far uglier than $var - the @ is denser and
> blockier, so it takes up a disproportionate amount of visual attention.
you realize that you two are discussing which of Ruby's scoping
specifiers are prettier than the other, or? this does not seem to me to
have very much to do with the original problem? can't we please keep
stuff like this out of the threads? it gets really boring following a
thread when it spans a myriad of subtrees of mildly related topics. and
in any case, using a global variable seems to be fine in such a short
script, but should of course be an instance variable in any well-formed
code. and, if you find the 'at' sign (@) to be too dense, using another
font, or creating your own would perhaps fix this?

Michael Campbell

unread,
Sep 15, 2003, 5:19:23 PM9/15/03
to

--- "Weirich, James" <James....@FMR.COM> wrote:
> > IMHO rather:
> > @facts = [0,1]
>
> Hmmm ... I'm pretty sure 0! = 1

It is, at least according to wolfram, which I'm inclined to believe.

http://mathworld.wolfram.com/Zero.html
http://mathworld.wolfram.com/Factorial.html

__________________________________
Do you Yahoo!?
Yahoo! SiteBuilder - Free, easy-to-use web site design software
http://sitebuilder.yahoo.com

Nikolai Weibull

unread,
Sep 15, 2003, 5:28:16 PM9/15/03
to
* Michael Campbell <michael_s...@yahoo.com> [Sep, 15 2003 23:20]:

> It is, at least according to wolfram, which I'm inclined to believe.
the real interesting thing is figuring out why it must be so,

Ben Giddings

unread,
Sep 15, 2003, 6:40:34 PM9/15/03
to
Alex Martelli wrote:
> Heh -- matter of taste, I guess; what _I_ personally find ugly is
> that leading $, and I left the underscores in to try to ameliorate
> that. Of course, nothing stops you from moving to a camelCaseish
> $factMemo -- however, not only do I personally find that less
> readable, but I'd also like to leave a hint that the memo is an
> internal implementation detail, not a feature meant for external
> consumption, and I like the convention of a leading _ for that.
> (Of course, I _could_ wrap up the fact function and its memo in
> a single package in any of various ways, I'm sure, so the "hint"
> aspect is quite secondary).

Sorry, I wasn't completely clear. It's the leading underscores that I
object to. I don't mind underscores_in_names at all. The reason I
dislike leading underscores is that they tend to be holdovers to
languages that don't have proper access control. (C mostly)

In any situation where someone is tempted to use them, I find that they
normally should be using something else (like using instance variables
which are by their nature private).

As for the ugliness of '$', it has been said that it is supposed to be
ugly. Global variables in general are ugly code, and so people tend to
avoid using '$', making their programs better.

> However, it will be very rare for any given x to NOT be already
> memoized (i.e., fact is called with a very modest variety of
> arguments, compared to the number of calls to it, in any typical
> combinatorial-arithmetic application); while calls with x<0 are
> going to be exceedingly rare. So, the relative costs of array
> indexing vs (operator< + conditional) shouldn't matter -- trying
> to get the result off the array first _should_ be a win anyway
> (or at least, that's what my optimization experience as built on
> a wide variety of languages tells me -- admittedly I have no such
> experience in Ruby, but I fail to see how it would differ on this
> specific point).

Ah, I see what you mean. I'm sure you're right that it's faster the way
you now have it. I didn't think things through fully and realize that
95% of the time the number will be positive and so it will have to
evaluate the conditional then do the array lookup. It is also going to
be *much* faster to do the array lookup rather than the triple
conditional for the "comb" case.

> Actually, I find the oneliner body
>
> $_fact_memo[x] ||= if x<0 then 0 else x * fact(x-1) end
>
> quite clear and readable, given that one must grok the semantics
> of ||= anyway.

Yeah. That "||=" is a little hard the first time you notice it, but
once you realize how useful it is, you'll use it so often it becomes
second nature.

You could also use the other form, which as a C++ coder I'm sure you'd
recognize:

$_fact_memo[x] ||= (x<0) ? 0 : fact(x-1)

Which also works in Ruby. I don't like it as much, but it's a
preference I guess.

> Very good point, and an excellent suggestion at least for concision &
> readability purposes -- I'll definitely keep the idiom
> <container>[<index>] ||= <value>
> in mind for the future -- thanks!

Note it also works for non container objects:

var ||= "new val";

and in other types of expression:

var = other_var || "default val"

> Performance-wise, there ain't all that much in it for this app.
>
> I've managed to scrounge some diskspace on Linux and compile both
> Python and Ruby (1.6.8 only, unfortunately -- the link I downloaded
> from mentioned 1.8, but I found out only when everything was built
> that I had 1.6.8 instead [how I found out: I had thoughtlessly left
> a trailing colon on an "if" statement -- 1.8 was quite cool about
> it, while 1.6.8 gave me three weird errors for that one mistake;-)].

If you have the chance to try it on Ruby 1.8 I (and others I'm sure)
would be interested in hearing how it stacks up both to Python and to
Ruby 1.6.

(In another post) Alex Martelli wrote:
>> While I doubt you should compare these languages on performance merits
> *blink* why not, pray? While clarity, productivity, simplicity, and so on,
> are surely all crucial attributes in a language, what's wrong with wanting
> to ascertain how it performs (when used in the best/fastest way, at least)
> in typical problems in one's domains of interest...?

I don't think that it's wrong to compare the speed of the languages. I
just wouldn't be surprised if Python is faster by a healthy margin,
especially for a very simple numerical program like this. In Ruby,
*everything* is an object, including numbers. This has certain
advantages, but definitely carries some speed penalties.

By using a scripting language rather than assembly or C, you've already
decided you're willing to sacrifice some performance for ease of use. I
expect that both Perl and Python would beat Ruby in nearly any
benchmark, but I find Ruby much easier to use than both. For tasks
where a scripting language is appropriate, I choose Ruby.

Mark Wilson

unread,
Sep 15, 2003, 6:43:52 PM9/15/03
to

On Monday, September 15, 2003, at 05:15 PM, Nikolai Weibull wrote:

> * Martin DeMello <martin...@yahoo.com> [Sep, 15 2003 23:10]:
>>> It just makes it worse. The $ is ugly for a reason--the use of
>>> global variables is discouraged. The Ruby Way to do it would be
>>
>> Ironically, I find @var far uglier than $var - the @ is denser and
>> blockier, so it takes up a disproportionate amount of visual
>> attention.
> you realize that you two are discussing which of Ruby's scoping
> specifiers are prettier than the other, or? this does not seem to me
> to
> have very much to do with the original problem? can't we please keep
> stuff like this out of the threads? it gets really boring following a
> thread when it spans a myriad of subtrees of mildly related topics.
> and
> in any case, using a global variable seems to be fine in such a short
> script, but should of course be an instance variable in any well-formed
> code. and, if you find the 'at' sign (@) to be too dense, using
> another
> font, or creating your own would perhaps fix this?

> [snip]

lol. Aesthetically, I prefer @ to $. One is circular, the other is
serpentine. One represents the spiritual, the other lucre.
Aesthetically, I like a little humor in the threads.

Regards,

Mark


Ben Giddings

unread,
Sep 15, 2003, 6:48:00 PM9/15/03
to

Its ugliness is good too -- it is good practice to avoid accessing your
own instance variables directly by using an accessor methods instead.
If you do this, you get better code and fewer "@"s. Sure, you'll still
have more "@"s than "$"s, but dont think that "@" is good and "$" is bad.

Ben

Michael Campbell

unread,
Sep 15, 2003, 7:42:59 PM9/15/03
to

--- Nikolai Weibull <lone...@home.se> wrote:
> * Michael Campbell <michael_s...@yahoo.com> [Sep, 15 2003
> 23:20]:
> > It is, at least according to wolfram, which I'm inclined to
> > believe.

> the real interesting thing is figuring out why it must be so,


Undoubtedly, but that's a bit beyond me, I'm afraid. =)

Hal Fulton

unread,
Sep 15, 2003, 7:48:56 PM9/15/03
to
Michael Campbell wrote:
> --- Nikolai Weibull <lone...@home.se> wrote:
>
>>* Michael Campbell <michael_s...@yahoo.com> [Sep, 15 2003
>>23:20]:
>>
>>>It is, at least according to wolfram, which I'm inclined to
>>>believe.
>
>
>>the real interesting thing is figuring out why it must be so,
>
>
> Undoubtedly, but that's a bit beyond me, I'm afraid. =)

Not particularly. It's one of those things where, once you
see it, it's no big deal.

Unfortunately, I can't remember exactly how it works.

There are formulae that involve (n-k)!, perhaps in the
denominator, I can't recall. And in the degenerate case
where n=k, "common sense" tells us that we get the right
answer if 0! is arbitrarily defined to be 1. So we do that.

At least, I think that's how it works. The more math
background you have, the more you may grind your teeth at
my generalization.

Hal


Mark Wilson

unread,
Sep 15, 2003, 8:49:59 PM9/15/03
to
0! represents the number of possible permutations of 0 elements, which
is 1.

See:

http://mathworld.wolfram.com/Factorial.html

Regards,

Mark


Scott Thompson

unread,
Sep 15, 2003, 10:11:13 PM9/15/03
to
Mark Wilson wrote:
> 0! represents the number of possible permutations of 0 elements, which
> is 1.

Well either that or ZERO!, NONE!, I mean it!

Seems that that's what I find myself shouting at my 2 year old anyway.

Scott


Robert Klemme

unread,
Sep 16, 2003, 4:18:26 AM9/16/03
to

"Robert Feldt" <fe...@ce.chalmers.se> schrieb im Newsbeitrag
news:oprvj614...@mail1.telia.com...

> Robert Klemme <bob....@gmx.net> skrev den Tue, 16 Sep 2003 05:08:35
+0900:
>
> >> def fact(n)
> >> @facts[n] ||= (@facts.length..n).inject(@facts.last) {|f, i|
@facts[i] =
> > i * f}
> >> end
> >
> > And, I think there's a subtle bug with the inject parameter. Another
> > version:
> >
> > def fact(n)
> > @facts ||= [0,1]
> > @facts[n] ||= (@facts.length..n).inject(@facts[-1]) {|f, i| @facts[i]
= i
> > * f}
> > end
> >
> Can you spell it out for me? I fail to see the difference.

Argh! *My* fault! I read "@facts.last" as "last index", but it's "last
element". Oh just forget it...

/me scuffles away into some corner and coils up.

> And I wouldn't do the initial assignment inside the method when
> we're discussing performance...

Yeah. I just put it there to ensure proper operation if someone copied
and pasted the code somewhere else.

Regards

robert

Robert Klemme

unread,
Sep 16, 2003, 4:25:49 AM9/16/03
to

"Weirich, James" <James....@FMR.COM> schrieb im Newsbeitrag
news:1C8557C418C56142999...@MSGDALCLB2WIN.DMN1.FMR.COM...

> > IMHO rather:
> > @facts = [0,1]
>
> Hmmm ... I'm pretty sure 0! = 1

Thanks for refreshing my math knowledge!

robert

Robert Klemme

unread,
Sep 16, 2003, 4:27:57 AM9/16/03
to

"Nikolai Weibull" <lone...@home.se> schrieb im Newsbeitrag
news:2003091521...@puritan.pcp.ath.cx...

> * Michael Campbell <michael_s...@yahoo.com> [Sep, 15 2003 23:20]:
> > It is, at least according to wolfram, which I'm inclined to believe.
> the real interesting thing is figuring out why it must be so,
> nikolai

Maybe because of the combinatorial formula n over k = n! * (n-k)! / k!
which for k == n leads to n! * 0! / n! which should be 1 and not 0.

Cheers

robert

Robert Klemme

unread,
Sep 16, 2003, 4:20:40 AM9/16/03
to

"Ben Giddings" <bg-ru...@infofiend.com> schrieb im Newsbeitrag
news:3F66416C...@infofiend.com...

I didn't measure this but I'd guess that a direct access is indeed faster
(mind the subject). :-)

robert

Alex Martelli

unread,
Sep 16, 2003, 11:48:04 AM9/16/03
to
Mark Wilson wrote:
...

> lol. Aesthetically, I prefer @ to $. One is circular, the other is
> serpentine. One represents the spiritual, the other lucre.

Surely you're jesting? The fine $ symbol is the stylized personal
emblem of Sigismondo Malatesta, the Signore of Rimini (and many other
parts in Romagna, too). It's an S, for Sigismondo, entwined with a
I, for Isabella, his life-long lover (I believe he did marry her on
her deathbed, actually, so she was his wife for a very short while).

When not totally stylzed, the I is generally depicted as a tree, and
the S a snake climbing the tree -- the tree of knowledge in the
garden of Eden, and the snake tempting man to reach for knowledge
of Good and Evil that apparently he was not meant to possess.

BTW, the best place to admire the $ symbol and reflect on its deep
symbolism and long history is of course the Tempio Malatestiano in
Rimini -- personally I classify it as the masterpiece of Leon Battista
Alberti, and among the true gems of the Italian Rinascimento revival
of architecture. You'll see the tree-and-snake painted all over the
place, of course.

Nor do I have anything against serpentine symbolism, mind you (would
anyone expect me to, after all?). I just prefer the $ not to abut
other symbols (such as letters and digits) too directly, whence the
preference for $_foo over $foo.


> Aesthetically, I like a little humor in the threads.

I did think of proposing some outrageous joke, such that the "$"
stood for "dollar", but given the obvious differences between a S
and a d nobody would of course fall for that. You'd almost think
the symbol was chosen by a bunch of freemason freethinkers who
were aware of the Malatestas' mostly-hidden roles in freemasonry,
Rosicrucianism, and other anti-clerical movements through the
centuries, starting with the Renaissance's rediscovery of Pagan
classicism. Nah, nobody would ever fall for THAT one, either.


Alex

Alex Martelli

unread,
Sep 16, 2003, 1:02:07 PM9/16/03
to
Ben Giddings wrote:
...

> Alex Martelli wrote:
>> Heh -- matter of taste, I guess; what _I_ personally find ugly is
...

> Sorry, I wasn't completely clear. It's the leading underscores that I
> object to. I don't mind underscores_in_names at all. The reason I
> dislike leading underscores is that they tend to be holdovers to
> languages that don't have proper access control. (C mostly)

Or Python -- no "access control" whatsoever, just *advisory* indications
of what's meant to be "published" and what's meant to be for internal use
only. Just the time saved not having to decide what's private, public,
or protected (Stroustrup regrets having introduced the complication of
that third intermediate classifier -- see his book "Design and Evolution
of the C++ programming language") is by itself a huge performance boost.


> In any situation where someone is tempted to use them, I find that they
> normally should be using something else (like using instance variables
> which are by their nature private).

Given that in Ruby one can always, anywhere, reopen a class and add
getters and setters for all attributes of interest in a few keystrokes,
I guess the "by their nature private" issue doesn't cost you much (except
some wasted performance, perhaps?) but doesn't buy you much either (no
more than in C++ with its typical "#define private public" trick, say).

Java, at least in theory, _does_ enforce privacy strictly (but I've
seen too many security issues with JVM's to trust it, personally;-),
so there are "real" costs and benefits involved; the same might be said
of a very few implementations of C++ (I'm thinking of SOM, but it's SO
many years since I used it that I may have the details wrong -- clearly
the strict enforcement didn't do much for the market success of those
implementations of C++ -- against MS, Borland, and gcc, say;-). But as
long as the "protection" is fundamentally just advisory, I don't think
there's much in it, either as a cost or as a benefit. I'd rather have
a simpler language relying on a simple naming convention than a more
complicated language with elaborate advisory mechanisms, personally.


> As for the ugliness of '$', it has been said that it is supposed to be
> ugly. Global variables in general are ugly code, and so people tend to
> avoid using '$', making their programs better.

I have nothing against this line of reasoning. I'm still allowed to
make the code a BIT less ugly (in my eyes) by using $_foo instead of
base $foo, though. By the same token, literals in your code are ugly
(they should all be constants, right?) -- so do you object to Ruby
letting me write a prettier (and more readable, IMHO) 1_000_000
rather than an uglier (harder to read, must count 0's) 1000000 ...?-)


>> However, it will be very rare for any given x to NOT be already
>> memoized (i.e., fact is called with a very modest variety of
>> arguments, compared to the number of calls to it, in any typical
>> combinatorial-arithmetic application); while calls with x<0 are
>> going to be exceedingly rare. So, the relative costs of array
>> indexing vs (operator< + conditional) shouldn't matter -- trying
>> to get the result off the array first _should_ be a win anyway
>> (or at least, that's what my optimization experience as built on
>> a wide variety of languages tells me -- admittedly I have no such
>> experience in Ruby, but I fail to see how it would differ on this
>> specific point).
>
> Ah, I see what you mean. I'm sure you're right that it's faster the way
> you now have it. I didn't think things through fully and realize that
> 95% of the time the number will be positive and so it will have to
> evaluate the conditional then do the array lookup. It is also going to
> be *much* faster to do the array lookup rather than the triple
> conditional for the "comb" case.

Ayup. I did try both ways, btw -- hard to measure the difference
in my current benchmark (it just doesn't exercise fact enough, and
comb barely enough), but fwiw it does seem to be faster with the
"if" inside. (Anybody who's done enough optimization will tell you
that rationalizing why A is faster than B is all nice and good, but
until and unless you've MEASURED them, you don't really KNOW...!-).


>> Actually, I find the oneliner body
>>
>> $_fact_memo[x] ||= if x<0 then 0 else x * fact(x-1) end
>>
>> quite clear and readable, given that one must grok the semantics
>> of ||= anyway.
>
> Yeah. That "||=" is a little hard the first time you notice it, but

Yeah, it _was_ a little hard, about 15 years ago I think, when I
first met it -- in Griswold's "Icon" programming language;-).

> once you realize how useful it is, you'll use it so often it becomes
> second nature.

No doubt. The Python equivalent (for a dict only, not a list, but
Python dicts are so fast that they're preferable to lists in all of
these cases -- yep I did measure both ways;-) would be:

return fact_memo.setdefault(x, ...whatever...)

BUT the applicative-order (vs normal-order -- aka eager vs lazy)
semantics of function calls means that "whatever" would have to be
computed in all cases (even when x IS already a key in fact_memo),
making this construct useful in substantially fewer cases.


> You could also use the other form, which as a C++ coder I'm sure you'd
> recognize:
>
> $_fact_memo[x] ||= (x<0) ? 0 : fact(x-1)
>
> Which also works in Ruby. I don't like it as much, but it's a
> preference I guess.

A dream I'll surely never be able to afford would be to have the same
projects tackled by pair of otherwise equivalent teams, but with two
languages that differ in just ONE characteristic -- surely, in theory,
that would be a wonderful way to measure the worth of that one
feature. Out of all the possible "ONE characteristic"s to be so
tested, the single one I'd be most interested in studying that way
(if I had a few megabucks to spare in order to pay the temas;-) might
be the enthusiastic "more than one way to do it" philosophy of Perl
and Ruby, vs the principle which the C Standard phrases as "provide
only one way to do an operation", and the Python Zen phrases as
"There should be one-- and preferably only one --obvious way to do it.".

My guess is that, while there might be tradeoffs depending on the
quality, personality, etc, of the programmers, for _small_ teams,
as teams grow larger, uniformity (with its attendant help in
moving towards the "collective code ownership" ideal of XP) would
no doubt become preferable. Of course, one could try to enforce
uniformity by other means (static code checkers, regular code
inspections, etc, etc), but they're all costlier than not having
the variability in the language in the first place (and not having
the variability also makes the language smaller and thus faster to
learn, to master, to implement -- again, a "spirit of C" principle,
"Keep the language small and simple", which I revere).

Oh well, it will no doubt remain an (educated) guess -- and the
uniformity, even in C or Python, is only an ideal, anyway.

But for the life of me I just can't see why, when one has
"if a then b else c end" working perfectly as both an expression
and a control statement, one would WANT to weigh down the language
with an alternative but equivalent syntax "a?b:c". I guess a
definitely-NOT-postmodern aesthetics which appreciates simplicity,
uniformity, etc, makes me characterially unsuited to appreciate
this "enthusiastic redundance" idea of Perl and Ruby!-)


>> Very good point, and an excellent suggestion at least for concision &
>> readability purposes -- I'll definitely keep the idiom
>> <container>[<index>] ||= <value>
>> in mind for the future -- thanks!
>
> Note it also works for non container objects:
>
> var ||= "new val";

Noted, thanks (I also notice the redundant trailing ";"""...?-) -- I
guess this means that keeping variables initially undefined may be
a sensible strategy in Ruby where it wouldn't be in C or Python.

> and in other types of expression:
>
> var = other_var || "default val"

Sure (but that's quite OK in Python too, since you can more legibly
spell "||" as "or" in this case, and then it does short-circuit --
the Python issue is that there's no shortcircuiting "or=", as well
as the fact that undefined variables raise exceptions rather than
quiety returning nil [or None, in Python]).


>> Performance-wise, there ain't all that much in it for this app.
>>
>> I've managed to scrounge some diskspace on Linux and compile both
>> Python and Ruby (1.6.8 only, unfortunately -- the link I downloaded
>> from mentioned 1.8, but I found out only when everything was built
>> that I had 1.6.8 instead [how I found out: I had thoughtlessly left
>> a trailing colon on an "if" statement -- 1.8 was quite cool about
>> it, while 1.6.8 gave me three weird errors for that one mistake;-)].
>
> If you have the chance to try it on Ruby 1.8 I (and others I'm sure)
> would be interested in hearing how it stacks up both to Python and to
> Ruby 1.6.

No doubt I will once I'm back home -- I find that I can't work on
this as much as I had hoped while on this business trip.


> (In another post) Alex Martelli wrote:
>>> While I doubt you should compare these languages on performance merits
>> *blink* why not, pray? While clarity, productivity, simplicity, and so
>> on, are surely all crucial attributes in a language, what's wrong with
>> wanting to ascertain how it performs (when used in the best/fastest way,
>> at least) in typical problems in one's domains of interest...?
>
> I don't think that it's wrong to compare the speed of the languages. I
> just wouldn't be surprised if Python is faster by a healthy margin,
> especially for a very simple numerical program like this. In Ruby,
> *everything* is an object, including numbers. This has certain
> advantages, but definitely carries some speed penalties.

I fail to see where the speed penalties are inherent, given that the
semantics of integer numbers in Python and Ruby are so similar now
(e.g., fixnums, aka "int", now transparently give bignum, aka "long",
results in Python, too). Indeed, in Python, there isn't that deep
a distinction between int and anything else as there seems to be in
Ruby between Fixnum (and true and false and nil - that's all I think?)
and everything else -- surely by singling out fixnums that way Ruby
IS gaining some speed advantage, at least for those computations (a
fair chunk) that stay within the roughly-one-billion limit in this
benchmark. Besides, it appears to me (from that one attempt to run
the profiler) that the bottleneck isn't in the arithmetic, anyway,
but rather in the generation of the subsets with that recursive
iterator in each language (so perhaps my next step should be some
attempt at recursion-elimination, muses he consequently). In other
words, I'm not really sure this IS "a very simple numerical program";
where I come from (Fortran, my first language, back then...) by "a
very simple numerical program" we mean something for which enumerating
subset by recursion would never qualify (let alone iterators).

Anyway, I'll grow this to definitely-NOT-simple eventually. Will
the extra complication of the problem being solved favour Ruby's
performance? Stay tuned...;-).

I'll accept your evaluation that you're not surprised if Python is
faster by a healthy margin, but I'd rather a different explanation
than the one you give. *WHAT* do you think is NOT "an object" in
the Python program, giving Python an intrinsic speed advantage...?

I can't see anything, myself. Indeed, I'd look at the reverse issue:
in Python "a tuple of numbers" is an object and thus can index right
into a dictionary -- in Ruby, I was informed, when it LOOKS like I'm
using a tuple to index into a Hash, in fact the interpreter is working
hard under the covers at forming a string out of that tuple -- so, it
seems to me, the performance handicap is that "tuple" is NOT an
object which can (directly) index a Ruby Hash (here I've fixed it, as
suggested, by using x<<16+y, rather than [x,y], as the key into the
Hash, but I wonder about what happens when I have several numbers
instead of just a pair of them -- as will soon be the case as my
example script grows more ambitious... but the numbers will be small,
so perhaps enough << and + will work well anyway -- we'll see!).


> By using a scripting language rather than assembly or C, you've already
> decided you're willing to sacrifice some performance for ease of use. I

You might be surprised. Try coding in standard C or assembly a program
that must work with integers as large as 52!, without using some finely
tuned external library for the pupose, and we'll see what performance
you do get...;-). [In practice, multiple-precision arithmetic is NOT
trivial to code with decent performance]. Sure, I could use GMP, say --
but then I could use it for Ruby or C just as well, and the variability
becomes unbounded. I _am_ interested in comparing standard languages
as opposed to languages coupled with any one of a zillion potential
add-on libraries, after all.

And I'm not interested in blowing up my program by an order of magnitude
so I can include in it complete sources for multi-precision arithmetic
and hash tables. I want to compare programs of comparable complexity,
thus, languages of comparable semantic level -- as indeed Python and
Ruby easily prove to be on this problem, with the code in the two
languages in such an obvious, nearly 1:1 correspondence. Given that the
semantic level is just about the same, performance discrepancies as
high as 2 or 3 times are _STILL_ pretty mysterious to me.

> expect that both Perl and Python would beat Ruby in nearly any
> benchmark, but I find Ruby much easier to use than both. For tasks
> where a scripting language is appropriate, I choose Ruby.

I know of no tasks where "a scripting language" is NOT appropriate,
except operating systems (kernels and drivers), and _accelerators_ for
"scripting languages" (and, in the pypy project, we're trying to drop
the second proviso, and prove that higher level languages CAN in
fact be made intrinsically faster than lower level ones -- but that's
a longer-term goal, of course;-). So, anyway, I'm trying to see the
"easier to use" part -- which includes, IMHO, performance issues where
appropriate. I can easily see it wrt Perl -- the OO, the fact that
the language is smaller and more elegant. But where does it come wrt
Python, given that _Python_ is the smaller language (no deliberate
redundance) and the "elegance" pro's and con's clearly go both ways
(Ruby has some very elegant parts, those designed from scratch, but
e.g. all of those $_ $` $' $! etc etc that it borrowed from Perl
surely can't be considered *elegant*...???)...? That's what I'm trying
to find out. Perhaps it's domain dependent and just doesn't show up
at all in combinatorial-arithmetic? OK, then once I've exhausted that
I'll move on to something else -- after all Python and Ruby are clearly
both perfectly general-purpose languages, so it's not as if I'll soon
run out of application domains to explore, hm?-)


Alex

Alex Martelli

unread,
Sep 16, 2003, 1:19:28 PM9/16/03
to
ts wrote:

>>>>>> "A" == Alex Martelli <ale...@yahoo.com> writes:
>
> A> Oh my! I had no idea Ruby suffered under such a handicap wrt to Python
> A> (that arrays had to be converted to strings to index into hashes, while
> A> Python can use "tuples", its equivalent of frozen arrays, for that) --
>
> svg% ruby -e 'a = {[1, 2] => 12}; p a.keys[0].class'
> Array
> svg%
>
> A> then I must definitely be very sparing in using hashes with multi-dim
> A> indexes in Ruby, at least in any bottleneck. Thanks! Just the kind of
> A> things I'm trying to learn
>
> then forget it :-)

...while remembering that some who post here may NOT be as knowledgeable
as Ruby as they sound by the certainty of their assertions (just like on
most other Usenet groups, of course). OK, thanks!


Alex

Alex Martelli

unread,
Sep 16, 2003, 1:17:20 PM9/16/03
to
Hal Fulton wrote:

> Alex Martelli wrote:
>> Simon Strandgaard wrote:
>> ...
>>
>>>>def comb(x, y)
>>>> # combinations of x things y at a time (0 if x<0, y<0, or y>x)
>>>> result = $_comb_memo[[x, y]]
>>>
>>> ^^^^^^
>>> ^^^^^^
>>>
>>> very slow!!
>>>
>>>Ruby converts it the array into a string.. this takes time!


>>
>>
>> Oh my! I had no idea Ruby suffered under such a handicap wrt to Python

>> (that arrays had to be converted to strings to index into hashes, while

...
> Simon is mistaken here. There may be some kind of
> speed hit when storing an array as a hash key, but
> the key is definitely stored as an array.

Ah! Good to know, thanks -- I was (mistakenly, of course) taking all the
pronouncements of the c.l.ruby readership as if they came from Ruby
experts -- unless of course corrected by others at the time I got to read
them (I always read news and mail backwards in time to cover for that),
while Simon's remark had been left unchallenged until now. Still, the
move from [x,y] to x<<16+y did accelerate Ruby a bit (while the same
change slowed Python down a bit) so it does seem the "some kind of speed
hit" may in certain cases be (though not by much) noticeable.


Alex

Jason Creighton

unread,
Sep 16, 2003, 12:02:36 PM9/16/03
to
On Tue, 16 Sep 2003 07:40:34 +0900
Ben Giddings <bg-ru...@infofiend.com> wrote:

> (In another post) Alex Martelli wrote:
> >> While I doubt you should compare these languages on performance merits
> > *blink* why not, pray? While clarity, productivity, simplicity, and so on,
> > are surely all crucial attributes in a language, what's wrong with wanting
> > to ascertain how it performs (when used in the best/fastest way, at least)
> > in typical problems in one's domains of interest...?
>
> I don't think that it's wrong to compare the speed of the languages. I
> just wouldn't be surprised if Python is faster by a healthy margin,
> especially for a very simple numerical program like this. In Ruby,
> *everything* is an object, including numbers. This has certain
> advantages, but definitely carries some speed penalties.

Err...isn't everything an object in Python as well?

~$ python
Python 2.3 (#1, Aug 4 2003, 08:45:40)
[GCC 3.2.2 (CRUX)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> n = 5.5
>>> dir(n)
['__abs__', '__add__', '__class__', '__cmp__', '__coerce__',
'__delattr__', '__div__', '__divmod__', '__doc__', '__float__',
'__floordiv__', '__getattribute__', '__getnewargs__', '__hash__',
'__init__', '__int__', '__long__', '__mod__', '__mul__', '__neg__',
'__new__', '__nonzero__', '__pos__', '__pow__', '__radd__', '__rdiv__',
'__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__',
'__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__',
'__rtruediv__', '__setattr__', '__str__', '__sub__', '__truediv__']
>>> n.__add__(4)
9.5
>>> n.__int__()
5

> By using a scripting language rather than assembly or C, you've already
> decided you're willing to sacrifice some performance for ease of use. I
> expect that both Perl and Python would beat Ruby in nearly any
> benchmark, but I find Ruby much easier to use than both. For tasks
> where a scripting language is appropriate, I choose Ruby.

Yes, I have found that both Perl and Python seem to be faster than Ruby
for running the code, but Ruby is faster for writing it. :)

Jason Creighton

Warren Brown

unread,
Sep 16, 2003, 5:16:21 PM9/16/03
to
Hal,

> Unfortunately, I can't remember exactly how
> it works.
>
> There are formulae that involve (n-k)!,
> perhaps in the denominator, I can't recall.
> And in the degenerate case where n=k,
> "common sense" tells us that we get the
> right answer if 0! is arbitrarily defined
> to be 1. So we do that.

Pretty much any way you define the factorial function, you always end up
with some form of the relationship:

X! = X * (X - 1)!

Or, put another way:

(X - 1)! = X! / X

So, for X == 1:

0! = 1! / 1

- Warren Brown


Warren Brown

unread,
Sep 16, 2003, 5:53:52 PM9/16/03
to
Alex,

> Surely you're jesting? The fine $ symbol is
> the stylized personal emblem of Sigismondo
> Malatesta, the Signore of Rimini (and many
> other parts in Romagna, too). It's an S,
> for Sigismondo, entwined with a I, for
> Isabella, his life-long lover (I believe he
> did marry her on her deathbed, actually, so
> she was his wife for a very short while).

Interesting. I have never heard it suggested that the Malatesta "logo"
was the inspiration for the dollar sign. Do you have any reference to back
up this claim? I'm not being antagonistic here, I am genuinely curious.

> I did think of proposing some outrageous
> joke, such that the "$" stood for "dollar",
> but given the obvious differences between a
> S and a d nobody would of course fall for
> that. You'd almost think the symbol was
> chosen by a bunch of freemason freethinkers
> who were aware of the Malatestas'
> mostly-hidden roles in freemasonry,
> Rosicrucianism, and other anti-clerical
> movements through the centuries, starting
> with the Renaissance's rediscovery of Pagan
> classicism. Nah, nobody would ever fall
> for THAT one, either.

Well FWIW, I have always heard that the origin of the dollar sign was
early United States currency. Originally, this currency had a fairly thin
capital "U" overlaying a capital "S" to indicate that it was United States,
or "US" currency. Eventually the US ligature degenerated into the
double-barred dollar sign which was ubiquitous before the typewriter, and
later the computer, degenerated it even further into the single-barred
dollar sign "$" we use today. I have heard other theories, and many of them
even claim that the dollar sign predates 1776, although usually by only a
few years. But I have yet to hear anyone discount the "US" theory or
present a more plausible one.

If anyone has anything more definitive, I would be interested in hearing
about it.

- Warren Brown


Hal Fulton

unread,
Sep 16, 2003, 6:40:14 PM9/16/03
to
Warren Brown wrote:
> Interesting. I have never heard it suggested that the Malatesta "logo"
> was the inspiration for the dollar sign. Do you have any reference to back
> up this claim? I'm not being antagonistic here, I am genuinely curious.

Me, too. I've never heard the name Malatesta except in an opera
I saw when I was twelve. Seriously. (Italian opera translated into
English -- forget which.)

> Well FWIW, I have always heard that the origin of the dollar sign was
> early United States currency. Originally, this currency had a fairly thin
> capital "U" overlaying a capital "S" to indicate that it was United States,
> or "US" currency. Eventually the US ligature degenerated into the
> double-barred dollar sign which was ubiquitous before the typewriter, and
> later the computer, degenerated it even further into the single-barred
> dollar sign "$" we use today. I have heard other theories, and many of them
> even claim that the dollar sign predates 1776, although usually by only a
> few years. But I have yet to hear anyone discount the "US" theory or
> present a more plausible one.
>
> If anyone has anything more definitive, I would be interested in hearing
> about it.

I read once that the inventor/introducer of the dollar sign (and I've
lost the reference) is buried in (my home state of) Mississippi.

But if the dollar sign comes from Italy, then, umm, there must be
someone else in that grave. :D

Hal


Ben Giddings

unread,
Sep 16, 2003, 7:32:10 PM9/16/03
to
Alex Martelli wrote:
> Or Python -- no "access control" whatsoever, just *advisory* indications
> of what's meant to be "published" and what's meant to be for internal use
> only.

Well, that's *technically* true of Ruby as well. You can get access to
a private variable at any time, if you try hard enough (using
instance_eval for example). It's just that you're encouraged to use
getter/setter methods, and to provide them for only the attributes you
want exposed.

I think the only thing it buys you is that it forces you to consider
"which of these attributes is internal state, and which do I want others
to have access to?" whenever you're writing something. My guess would
be that that lends itself to a cleaner design. It seems to me that the
way Python works lends you to "I'll make Foo with attributes bar and
bat, now which ones of these are really actually private?" then adding
an underscore to their names vs. "I'll make Foo with attributes bar and
bat, now which ones of these are actually public?" then adding an
accessor for that variable.

Afterall, there's nothing stopping you from using structs instead of
classes in C++. My understanding is that the only difference is that a
struct is by default all public, and a class is by default all private.
But people always use classes. Why? Probably that's just the way
they learned, but maybe it's because it's more natural to start with
everything private, then expose only what you think is necessary.

> I have nothing against this line of reasoning. I'm still allowed to
> make the code a BIT less ugly (in my eyes) by using $_foo instead of
> base $foo, though. By the same token, literals in your code are ugly
> (they should all be constants, right?) -- so do you object to Ruby
> letting me write a prettier (and more readable, IMHO) 1_000_000
> rather than an uglier (harder to read, must count 0's) 1000000 ...?-)

Yes, I think most literals are bad, and using constants is good, within
reason. I'd certainly cringe at "if x < THE_NUMBER_ZERO" though. Do
whatcha want when it comes to $_var, I just think it's ugly, but I think
a lot of people's code is ugly, so don't mind me. :)

> be the enthusiastic "more than one way to do it" philosophy of Perl
> and Ruby, vs the principle which the C Standard phrases as "provide
> only one way to do an operation", and the Python Zen phrases as
> "There should be one-- and preferably only one --obvious way to do it.".

Actually, my understanding of matz' design philosophy is that it's more
like Python's than Perl's. My understanding was that Python's was more
like "There should be one -- and preferably only one -- way to do it.",
(i.e. the obviousness didn't come into it) where Ruby's was "There
should be one obvious way to do it (but other ways are ok too)" So in
Python there should never be two ways of doing something, unless
absolutely necessary, but in Ruby there can easily be multiple ways of
doing something, but one should be the obvious choice. You know Python
far better than I do though, so maybe my understanding is wrong, but I
can say with confidence that Ruby has many more obvious solutions than
does Perl.

>>Note it also works for non container objects:
>>
>>var ||= "new val";
>
> Noted, thanks (I also notice the redundant trailing ";"""...?-) -- I
> guess this means that keeping variables initially undefined may be
> a sensible strategy in Ruby where it wouldn't be in C or Python.

Oops! An artifact of coding all day in C, I'm afraid. And a nitpick,
in Ruby variables aren't quite initially undefined, they're set to
"nil", the instance of the Nil class. At least, that's my
understanding. I don't think you can have a truly undefined variable in
Ruby.

> I fail to see where the speed penalties are inherent, given that the
> semantics of integer numbers in Python and Ruby are so similar now
> (e.g., fixnums, aka "int", now transparently give bignum, aka "long",
> results in Python, too).

Sorry, most of my python knowledge is way out of date. I used it for a
while seriously in '97, but not too much since then. If I recall
correctly, at that point there was a clear distinction between built-in
types and classes, and there were also certain classes which couldn't be
subclassed. But I have heard that changed.

> Anyway, I'll grow this to definitely-NOT-simple eventually. Will
> the extra complication of the problem being solved favour Ruby's
> performance? Stay tuned...;-).

I'd also be curious to have your impressions on things other than speed,
like how long it took to write the programs, how maintainable they seem
to be, how easy they are to modify, that sort of thing. It's a bit of
an unfair comparison since you know Python well, but this is the sort of
area that I think Ruby tends to win out in.

> I'll accept your evaluation that you're not surprised if Python is
> faster by a healthy margin, but I'd rather a different explanation
> than the one you give. *WHAT* do you think is NOT "an object" in
> the Python program, giving Python an intrinsic speed advantage...?

I haven't used Python 2.3 yet, and haven't used much Python at all in a
while, but I was under the impression that integers and other basic
types weren't quite full-fledged objects. I seem to remember that they
could act as objects, but there was still a distinction like Java's
"int" type vs. "Integer" class. Or maybe it was that they didn't all
seem to inherit from some base "Object" class. Maybe I'm completely
wrong though. I'd like to know more though.

> into a dictionary -- in Ruby, I was informed, when it LOOKS like I'm
> using a tuple to index into a Hash, in fact the interpreter is working
> hard under the covers at forming a string out of that tuple

Actually, that was a misconception, the person who told you that was
later corrected. I think what happens is that the object you're using
as a hash index simply has its "hash" method called (which all objects
get since it's defined in Object.

Anyhow, if it isn't the object-nature of the languages that causes the
speed difference, my guess is that it's the age, maturity, and
interpreter differences. Python's interpreter has probably had 5x the
man-hours put into it that Ruby's has had, so there's still lots of room
for tweaking improvement. I also think there's a fundamental difference
in the interpreter, based on the fact you can have .pyc files, and .pyo
files, but there's no such thing as a .rbc or a .rbo file. But my
knowledge of the internals of either interpreter is basically nonexistant.

> You might be surprised. Try coding in standard C or assembly a program
> that must work with integers as large as 52!, without using some finely
> tuned external library for the pupose, and we'll see what performance
> you do get...;-). [In practice, multiple-precision arithmetic is NOT
> trivial to code with decent performance]. Sure, I could use GMP, say --
> but then I could use it for Ruby or C just as well, and the variability
> becomes unbounded. I _am_ interested in comparing standard languages
> as opposed to languages coupled with any one of a zillion potential
> add-on libraries, after all.

Ok, but by that same token, if Python's multiple-precision arithmetic
support has had a lot more work than Ruby's, it's bound to be much
faster. If so, it wouldn't be a big surprise if Python 2.3's arithmetic
was twice as fast as that of Ruby 1.6. Maybe that's the cause of the
slowdown. In any case, I wouldn't get too hung up on it. For the
things I tend to use Ruby for, speed isn't an issue at all. It has
easily reached the point where it is "fast enough".

> I know of no tasks where "a scripting language" is NOT appropriate,
> except operating systems (kernels and drivers), and _accelerators_ for
> "scripting languages"

Unfortunately, I do, in particular embedded systems where code size and
memory size is at a premium. With the stuff I'm working on, we're using
scripting languages where we can, to test, prototype, etc. but in the
end we won't have any embedded scripting language on the current product.

> (Ruby has some very elegant parts, those designed from scratch, but
> e.g. all of those $_ $` $' $! etc etc that it borrowed from Perl
> surely can't be considered *elegant*...???)...?

No, certainly not. But then again, I have yet to come across a Ruby
program which uses them. In fact, I often argue against including them
in future versions of Ruby, as well as some of the non-oo-like Kernel
methods like "open" and "gsub" which are also Perl cruft.

I'm curious though, how do you know that Ruby has $_, $` and friends?
Did you see it in a book, online, in a critique of Ruby, or in code?
Like I said, I've never seen them in code, but they're mentioned in
books, and always used in critiques of Ruby. To me, criticizing $` in
Ruby is like criticizing C for having Duff's Device.

> I'll move on to something else -- after all Python and Ruby are clearly
> both perfectly general-purpose languages, so it's not as if I'll soon
> run out of application domains to explore, hm?-)

Nope, and it's fun to hear your reports of your explorations. I don't
have much time these days to do my own explorations. The only thing I'd
like better is if you'd explore what I find interesting, rather than
what you find interesting... but that's probably asking a bit too much,
isn't it? ;)

Ben


Bjarne Stroustrup

unread,
Sep 16, 2003, 9:37:03 PM9/16/03
to
Alex Martelli <ale...@yahoo.com>

> Or Python -- no "access control" whatsoever, just *advisory* indications
> of what's meant to be "published" and what's meant to be for internal use
> only. Just the time saved not having to decide what's private, public,
> or protected (Stroustrup regrets having introduced the complication of
> that third intermediate classifier -- see his book "Design and Evolution
> of the C++ programming language") is by itself a huge performance boost.
>

If you have actually read D&E, you'd know that I disagree with your
statement. Even if you want to quote me in support of something I
disagree with, you could try to quote accurately. Your statement of my
position on "protected" is highly inaccurate.

- Bjarne Stroustrup; http://www.research.att.com/~bs

Kent R. Spillner

unread,
Sep 16, 2003, 10:12:49 PM9/16/03
to
Bjarne Stroustrup wrote:
> If you have actually read D&E, you'd know that I disagree with your
> statement. Even if you want to quote me in support of something I
> disagree with, you could try to quote accurately. Your statement of my
> position on "protected" is highly inaccurate.

This is off-topic, but the obvious question is: "Do you Ruby?"
;-)

> - Bjarne Stroustrup; http://www.research.att.com/~bs

-Kent


Hal Fulton

unread,
Sep 16, 2003, 10:41:09 PM9/16/03
to

For the benefit of those of us who don't own that book (though I
do own at least one other) -- can you tell us what in fact your
position is on "protected"?

Hal Fulton


Martin DeMello

unread,
Sep 17, 2003, 2:50:40 AM9/17/03
to
Alex Martelli <ale...@yahoo.com> wrote:
>
> I did think of proposing some outrageous joke, such that the "$"
> stood for "dollar", but given the obvious differences between a S
> and a d nobody would of course fall for that. You'd almost think

Actually, the $ symbol is a stylised S, for String, and has its roots in
BASIC, as ane fule kno.

martin

gabriele renzi

unread,
Sep 17, 2003, 4:34:17 AM9/17/03
to

>> I know of no tasks where "a scripting language" is NOT appropriate,
>> except operating systems (kernels and drivers), and _accelerators_ for
>> "scripting languages"
>
>Unfortunately, I do, in particular embedded systems where code size and
>memory size is at a premium. With the stuff I'm working on, we're using
>scripting languages where we can, to test, prototype, etc. but in the
>end we won't have any embedded scripting language on the current product.

on a sidenote: I remember on of the first embedded linux distribution
(possibly ETLinux) that was base on Tcl. The author of it highlighted
that having a quite fast interpreter with low memory footprint was
acceptable, noted that you could save much more space for the other
programs, cause an interpreted language script is far more 'dense'
that compiled C or whatever.
(btw, if Ruby 1.8 was used you'll get the standard unix tools for free
, as in cd,cat,sort,grep...)

what do you think of it ?

Robert Klemme

unread,
Sep 17, 2003, 5:09:58 AM9/17/03
to

"Hal Fulton" <hal...@hypermetrics.com> schrieb im Newsbeitrag
news:3F67C9CA...@hypermetrics.com...

While eagerly awayting clarification from Bjarne, I googled a bit and this
is what found:
http://www.c-view.org/tech/pattern/cpptips/prot_data

His main concern is about protected data making it all too easy to mess
things up, whereas he favours protected methods.

Regards

robert

Bjarne Stroustrup

unread,
Sep 17, 2003, 9:20:28 AM9/17/03
to
Hal Fulton <hal...@hypermetrics.com> :

>
> For the benefit of those of us who don't own that book (though I
> do own at least one other) -- can you tell us what in fact your
> position is on "protected"?
>

I have no idea if my opinion makes sense in the context of ruby, but
in C++ "protected" is useful for functions and types, but people who
use it to "protect" data, find that the maintenance problems are
equivalent to publice data. I recommend to keep data private unless
you absolutely need it public.

Ruby Ruby

unread,
Sep 17, 2003, 9:39:58 AM9/17/03
to
Is there any IDE for FXRuby?

Thank you

Thomas Adam

unread,
Sep 17, 2003, 9:43:18 AM9/17/03
to
--- Ruby Ruby <ruby4...@yahoo.com> wrote:

> Is there any IDE for FXRuby?

Look here:

http://www.linuxmafia.com/~rick/linux-info/applications-ides.html

(find Ruby). If I were you I'd just use "gvim" (or vim) using quickfix.

HTH,

-- Thomas Adam



> Thank you
>
> __________________________________
> Do you Yahoo!?
> Yahoo! SiteBuilder - Free, easy-to-use web site design software
> http://sitebuilder.yahoo.com
>

=====
Thomas Adam

"The Linux Weekend Mechanic" -- www.linuxgazette.com

________________________________________________________________________
Want to chat instantly with your online friends? Get the FREE Yahoo!
Messenger http://mail.messenger.yahoo.co.uk

Ruby Ruby

unread,
Sep 17, 2003, 10:06:57 AM9/17/03
to
I looked at the link you posted below and found two
entries. The first entry was for BlackAdder and the
second entry, WideStudio. The BlackAdder says: (A
Ruby interpreter, debugger and ODBC support for Ruby
are not yet available. (Ruby is temporarily removed
until the Qt bindings are updated to Qt3, currently in
progress). BlackAdder is not free.

WideStudio is free and it claims to support ruby. I
will download it today and give it a try.
gvim/vim I use it as my exclusive Editor. I did not
know that it could be use for GUI programming.

I wanted to use FXRuby because it appears to run
everywhere, but I don't want to be concerned with the
internal details about defining widgets.

Thank you for the information.

Thomas Adam

unread,
Sep 17, 2003, 10:12:17 AM9/17/03
to
--- Ruby Ruby <ruby4...@yahoo.com> wrote:

> I looked at the link you posted below and found two
> entries.

Yes, that's how many my eyes counted, too :)

> The first entry was for BlackAdder and the
> second entry, WideStudio. The BlackAdder says: (A
> Ruby interpreter, debugger and ODBC support for Ruby
> are not yet available. (Ruby is temporarily removed
> until the Qt bindings are updated to Qt3, currently in
> progress). BlackAdder is not free.
>
> WideStudio is free and it claims to support ruby. I
> will download it today and give it a try.

:) Simplist thing to do :)

> gvim/vim I use it as my exclusive Editor. I did not
> know that it could be use for GUI programming.

But of course!! Vim supports syntax highlighting :) and quickfix mode is
excellent :)

> I wanted to use FXRuby because it appears to run
> everywhere, but I don't want to be concerned with the
> internal details about defining widgets.

Then try looking at some of the more general editors for FX on the link I
gave you previously. Perhaps they'll help?

> Thank you for the information.

Not at all, you're most welcome.

-- Thomas Adam

Lyle Johnson

unread,
Sep 17, 2003, 11:44:05 AM9/17/03
to
Ruby Ruby wrote:

> Is there any IDE for FXRuby?

I think that the question you are really asking is, "Are there any GUI
builder/dialog editor tools for FXRuby?" And the answer to that question
is, "Not yet" (although I hear whispers here and there of people working
on such tools).

Ben Giddings

unread,
Sep 17, 2003, 1:44:37 PM9/17/03
to
gabriele renzi wrote:
> programs, cause an interpreted language script is far more 'dense'
> that compiled C or whatever.
> (btw, if Ruby 1.8 was used you'll get the standard unix tools for free
> , as in cd,cat,sort,grep...)
>
> what do you think of it ?

ben@magneto% du --max-depth=0 /usr/lib/ruby
3.1M /usr/lib/ruby
ben@magneto% ll busybox
-rwxr-xr-x 1 ben ben 239K Sep 2 10:13 busybox

ben@magneto% ll |grep busybox |ruby -n -e 'print split(/\s/)[-3 .. -1],
"\n"'
210:13busybox
cat->busybox
chgrp->busybox
chmod->busybox
chown->busybox
cp->busybox
date->busybox
dd->busybox
df->busybox
dmesg->busybox
echo->busybox
false->busybox
getopt->busybox
grep->busybox
gunzip->busybox
gzip->busybox
hostname->busybox
kill->busybox
ln->busybox
ls->busybox
mkdir->busybox
mknod->busybox
mktemp->busybox
more->busybox
mount->busybox
mv->busybox
pidof->busybox
ps->busybox
pwd->busybox
rm->busybox
rmdir->busybox
sed->busybox
sleep->busybox
stty->busybox
sync->busybox
tar->busybox
touch->busybox
true->busybox
umount->busybox
uname->busybox
usleep->busybox
vi->busybox
zcat->busybox

Busybox does nearly every system administration type command I need, and
uses only 200K, Ruby uses over 3MB and that's a huge chunk of what I
have to play with. I haven't given up wanting to use it, but odds are I
just won't have room.

Ben


0 new messages